package com.xh.demo.handler.predicate;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.support.ShortcutConfigurable;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import javax.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Predicate;

/**
 * @author wen
 * @apiNote 自定义断言工厂实现
 * <p>
 * 1、必须添加到IOC容器
 * 2、约定：Test（前缀） + RoutePredicateFactory（后缀）
 * 3、继承 {@link AbstractRoutePredicateFactory}类
 * 4、必须定义静态类部类 {@link Config}
 * ....例如：predicates:
 * ..........- Query=name,jack
 * ..类部类属性：param 会接收 name
 * ............regexp 会接收 jack（可以是正则匹配）
 * 5、配合{@link ShortcutConfigurable#shortcutFieldOrder()}方法绑定 Config类的参数param和regexp
 * 6、通过{@link RoutePredicateFactory#apply(java.lang.Object)} 方法实现逻辑断言[true-通过 false-不通过]
 * <p>
 * @since 2021-12-12
 */
@Slf4j
@Component
public class TestRoutePredicateFactory extends AbstractRoutePredicateFactory<TestRoutePredicateFactory.Config> {

    /**
     * Param key.
     */
    public static final String PARAM_KEY = "key";

    /**
     * Regexp key.
     */
    public static final String REGEXP_KEY = "value";

    public TestRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        ThreadLocalRandom random = ThreadLocalRandom.current();
        log.info("【测试自定义路由断言工厂-TestRoutePredicateFactory】【{}】【{}】", config.getKey(), config.getValue());
        return exchange -> random.nextInt() % 2 == 0;
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(PARAM_KEY, REGEXP_KEY);
    }

    @Validated
    public static class Config {

        @NotEmpty
        private String key;

        private String value;

        public String getKey() {
            return key;
        }

        public TestRoutePredicateFactory.Config setKey(String key) {
            this.key = key;
            return this;
        }

        public String getValue() {
            return value;
        }

        public TestRoutePredicateFactory.Config setValue(String value) {
            this.value = value;
            return this;
        }
    }

}
